home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Tools - Objects / MacApp / MacApp 2.0 CD Release / MacApp 2.0 (Many Libraries) / Interfaces / PInterfaces / UFailure.p < prev    next >
Encoding:
Text File  |  1990-03-27  |  10.2 KB  |  261 lines  |  [TEXT/MPS ]

  1. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
  2. { UFailure.p }
  3. { Copyright © 1985-1990 by Apple Computer, Inc.  All rights reserved. }
  4.  
  5. {[f-]}
  6. (*
  7.                 T H E O R Y  O F     O P E R A T I O N
  8.  
  9.     This unit implements the MacApp failure mechanism.
  10.  
  11.     The failure mechanism is built around exception handlers.    An exception
  12.     handler is a routine, generally local to some other routine, that is
  13.     called when a failure occurs and takes action to handle the failure.
  14.     An exception handler is of the form
  15.  
  16.     PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
  17.  
  18.     where error is the error that caused the failure, and message identifies
  19.     the error message that may be displayed.  Consider a routine that opens
  20.     a file, reads its contents, and closes the file.  If a failure occured
  21.     while reading the file, an exception handler would be needed to close the
  22.     file, as the rest of the routine will not be executed.    (See the example
  23.     at the end of these comments.)
  24.  
  25.     References to expection handlers are defined by the FailInfo record.
  26.     The exception handlers form a linked-list via the nextInfo field of
  27.     FailInfo.    The linked list is a stack since new exception handlers
  28.     are added to the front of the list.
  29.  
  30.     New exception handlers are added to the stack with the CatchFailures
  31.     procedure, and removed from the stack with the Success procedure.    In
  32.     general you call CatchFailures to post an exception handler when an
  33.     error the application should handle might occur, and call Success to
  34.     remove the handler from the stack, after the handler is no longer
  35.     needed (i.e. the possibility of error no longer exists).  Any failure
  36.     detected within the limits of the CatchFailures and subsequent Success
  37.     call results in the execution of the exception handler.  (Failure does
  38.     not have to occur in the same routine as your call to CatchFailures.
  39.     The failure may occur in any routine called after CatchFailures but
  40.     before Success.)
  41.  
  42.     When MacApp (or your code) determines that a failure has occured, it
  43.     calls Failure.    As a convenience, several procedures are provided
  44.     to check for standard kinds of failures and call Failure if needed.
  45.     These procedures are:
  46.  
  47.     FailNIL         Calls Failure if its parameter is NIL.
  48.     FailOSErr        Calls Failure if its parameter is not noErr.
  49.     FailMemError    Calls Failure if MemError returns other than noErr.
  50.     FailResError    Calls Failure if ResError returns other than noErr.
  51.  
  52.     When Failure is called, execution of the routine that called Failure is
  53.     terminated and the exception handler at the top of the stack is popped.
  54.     For each routine that was called after the handler was posted
  55.     to the stack, execution is terminated as though from an EXIT statement.
  56.     Then the exception handler is called.    It generally cleans up for the
  57.     routine in which it is nested.    Upon completion the next exception handler
  58.     is popped from the stack, repeating the process.
  59.  
  60.     The error causing the failure, and a message code is passed to Failure.
  61.     For MacApp, the last exception handler on the stack is the one in
  62.     TApplication.PollEvent.  It calls TApplication.ShowError, which calls
  63.     ErrorAlert, which decodes the message and displays an alert.  You exception
  64.     handlers may set the message code to one more specific to your application
  65.     by calling FailNewMessage at the end of your exception handler.
  66.     FailNewMessage changes the message only if the current one is non-zero.
  67.     This has the effect of allowing those exception handlers closest to the
  68.     source of the error to set the message.
  69.  
  70.     One last note about exception handlers:  It is possible for an exception
  71.     handler to terminate exception processing by using a non-local GOTO to
  72.     jump back into the routine in which the exception handler is nested.  This
  73.     is how MacApp keeps the application running when a failure occurs.    The
  74.     last exception handler on the stack, in TApplication.PollEvent, uses a
  75.     GOTO to continue event processing.
  76.  
  77.     The following is an example showing the use of exception handlers.
  78.     The ReadTheFile procedure reads the contents of a file, signalling
  79.     failure if the open, read, or close returns an error.
  80.  
  81.     PROCEDURE ReadTheFile (fileName: Str255; volRefNum: INTEGER;
  82.                             VAR contents: ContentsRecord);
  83.     VAR
  84.         fi:         FailInfo;
  85.         count:        LONGINT;
  86.  
  87.         PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
  88.         VAR
  89.             err:    OSErr;
  90.         BEGIN
  91.             err := FSClose(fileRefNum);     { Make sure the file is closed }
  92.             { Pass on my own error message... }
  93.             FailNewMessage(error, message, kMyErrorMessage);
  94.         END;
  95.  
  96.     BEGIN
  97.         { If FSOpen fails we don't need to do any failure handling,
  98.             though the guy that called us probably will. }
  99.         FailOSErr(FSOpen(fileName, volRefNum, fileRefNum));
  100.  
  101.         { Now that the file is open, if the read fails we must make
  102.             sure to close the file. }
  103.         CatchFailures(fi, Exceptionhandler);
  104.  
  105.         count := SIZEOF(ContentsRecord);
  106.         FailOSErr(FSRead(fileRefNum, count, @contents));
  107.  
  108.         { The file's been read so we don't need our exception handler
  109.             anymore. }
  110.         Success(fi);
  111.  
  112.         { Again, the guy that called us will have to deal with this. }
  113.         FailOSErr(FSClose(fileRefNum));
  114.     END;
  115. *)
  116. {[f+]}
  117.  
  118. {$IFC UNDEFINED UsingIncludes}
  119. {$SETC UsingIncludes := FALSE}
  120. {$ENDC}
  121.  
  122. {$IFC NOT UsingIncludes}
  123. UNIT UFailure;
  124.  
  125.     INTERFACE
  126.         {$ENDC}
  127.  
  128.         {$IFC UNDEFINED __UFailure__}
  129.         {$SETC __UFailure__ := FALSE}
  130.         {$ENDC}
  131.  
  132.         {$IFC NOT __UFailure__}
  133.         {$SETC __UFailure__ := TRUE}
  134.  
  135.         { • Auto-Include the requirements for this unit's interface. }
  136.         {$SETC UFailureIncludes := UsingIncludes}
  137.         {$SETC UsingIncludes := TRUE}
  138.         {$I+}
  139.         {$IFC UNDEFINED UsingTypes} {$I Types.p} {$ENDC}
  140.         {$SETC UsingIncludes := UFailureIncludes}
  141.  
  142.         CONST
  143.             minErr                = - 32768;                { Low error number }
  144.             maxErr                = 32767;                { High error number }
  145.  
  146.         TYPE
  147.             { used for the exception handling mechanism }
  148.             FailInfoPtr         = ^FailInfo;            { Preferred. The pointer
  149.                                                         type _MUST_ be declared first since the record
  150.                                                         is self referential }
  151.             FailInfo            = RECORD
  152.                 regs:                ARRAY [1..11] OF LONGINT; { The saved registers as of the
  153.                                                                CatchFailures. }
  154.                 error:                INTEGER;            { The error condition passed to Failure. }
  155.                 message:            LONGINT;            { The message which accompanied the Failure.
  156.                                                          }
  157.                 failA6:             LONGINT;            { A6 of the caller to CatchFailures. }
  158.                 failPC:             LONGINT;            { Address of failure handler routine. }
  159.                 nextInfo:            FailInfoPtr;        { Next in stack. }
  160.                                 {$IFC qDebug}
  161.                 whoPC:                LONGINT;            { PC of the caller to CatchFailures. }
  162.                                 {$ENDC}
  163.                 END;
  164.             PFailInfo            = FailInfoPtr;            { Left in for compatibility (2.0) }
  165.  
  166.         VAR
  167.             {$IFC qDebug}
  168.             {$Push} {$J+}                                { Defined externally }
  169.             gAskAboutAlloc:     BOOLEAN;                { If TRUE, on every allocation ask whether
  170.                                                          to force failure. Useful in debugging to
  171.                                                          simulate "out of memory" conditions. }
  172.             gAskFailure:        BOOLEAN;                { If TRUE ask about calls to FailOSErr,
  173.                                                          FailResError, FailMemError, and give user
  174.                                                          a chance to force error. Useful in
  175.                                                          debugging to simulate these conditions. }
  176.             {$Pop}
  177.             {$ENDC qDebug}
  178.             {$Push} {$J+}                                { Defined externally }
  179.             gTopHandler:        FailInfoPtr;            { Most recent link in linked list of failure
  180.                                                          handlers. Set to nil by a constant in
  181.                                                          UFailure.a so that Failure handling never
  182.                                                          needs to be initialized. }
  183.             {$Pop}
  184.  
  185.         PROCEDURE Assertion(condition: BOOLEAN;
  186.                             description: StringPtr);
  187.         { Asserts a condition.If condition is FALSE : IF debugging : prints description and stops in
  188.         Debugger ELSE : Signals a general failure. }
  189.  
  190.         FUNCTION BuildMessage(lowWord, highWord: INTEGER): LONGINT;
  191.             INLINE $2E9F;                                { MOVE.L (A7)+,(A7) }
  192.         { Takes the 2 integers and combines them into a LONGINT failure message.  Note that the
  193.         low-order word is the first parameter. }
  194.  
  195.         PROCEDURE CatchFailures(VAR fi: FailInfo;
  196.                                 PROCEDURE Handler(e: INTEGER;
  197.                                                   m: LONGINT));
  198.         { Call this to set up an exception handler. This pushes your handler onto
  199.         a stack of exception handlers. }
  200.  
  201.         PROCEDURE EachFailureHandlerDo(PROCEDURE DoToHandler(fiPtr: FailInfoPtr));
  202.         { Calls DoToHandler for each failure handler in the stack from gTopHandler
  203.         to the outermost handler. }
  204.  
  205.         PROCEDURE Failure(error: INTEGER;
  206.                           message: LONGINT);
  207.         { Call this to signal a failure.  Control will branch to the most recent
  208.         exception handler, which will be popped off the handler stack. }
  209.  
  210.         PROCEDURE FailMemError;
  211.         { IF MemError <> noErr THEN Failure(MemError, 0); If you are using
  212.         assembler, then you should just test the return code from the Memory
  213.         Manager in DO by calling FailOSErr. (See the discussion of MemError in
  214.         Inside Macintosh.) }
  215.  
  216.         PROCEDURE FailResError;
  217.         { IF ResError <> noErr THEN Failure(ResError, 0); (See Inside Macintosh.) }
  218.  
  219.         PROCEDURE FailNewMessage(error: INTEGER;
  220.                                  oldMessage, newMessage: LONGINT);
  221.         { This does:
  222.         IF oldMessage = 0 THEN
  223.             Failure(error, newMessage)
  224.         ELSE
  225.             Failure(error, oldMessage);
  226.         }
  227.  
  228.         PROCEDURE FailNIL(p: UNIV Ptr);
  229.         { Call this with a pointer/handle; this signals Failure(memFullErr, 0) if p is NIL. }
  230.  
  231.         PROCEDURE FailNILResource(r: UNIV Handle);
  232.         { Call this with a resource handle; this signals Failure if the handle is nil. The error is
  233.         either that returned by ResError, or resNotFound if ResError returns no error. }
  234.  
  235.         PROCEDURE FailOSErr(error: INTEGER);
  236.         { Call this with an OSError; signals Failure(error, 0) if error <> noErr. }
  237.  
  238.         FUNCTION HandlerExists(testFailInfoPtr: FailInfoPtr): BOOLEAN;
  239.         { This test returns TRUE if the failure handler exists in the stack from gTopHandler
  240.         to the outermost handler.  }
  241.  
  242.         PROCEDURE Success(VAR fi: FailInfo);
  243.         { Call this when you want to pop your exception handler. We assume that the programmer passes
  244.         in the most recent FailInfo record; ie. the one that is the top of the stack. If debugging
  245.         is on, we check to be sure. }
  246.  
  247.         PROCEDURE ProgramBreak(grievance: Str255);
  248.         { ProgramBreak: Your app can call this when it comes to a situation that you do not expect
  249.         and cannot handle gracefully. It beeps and displays a message. If called before there is a
  250.         Transcript window then EnterMacAppDebugger will will call DebugStr (and end up in MacsBug).
  251.         Otherwise, it enters the MacApp debugger. }
  252.  
  253.         PROCEDURE ProgramReport(grievance: Str255;
  254.                                 break: BOOLEAN);
  255.         { Same as ProgramBreak but it only stops in debugger if break is TRUE. }
  256.         {$ENDC}
  257.  
  258.         {$IFC NOT UsingIncludes}
  259. END.
  260. {$ENDC}
  261.